CoCalc Logo Icon
DocsShareSupport News Sign UpSign In
Views: 15
Visibility: Unlisted (only visible to those who know the link)
Image: ubuntu2204
Embed | Download | Raw |
Kernel: Python 3 (ipykernel)
import random from IPython.core.display import SVG import pyomo.environ as pyo from pysat.solvers import Solver from pysat.formula import CNF #import py_svg_combinatorics as psc from ipywidgets import widgets, HBox from collections import Counter from pprint import pprint from random import randint import numpy as np from IPython.display import IFrame import IPython from copy import copy import os from pathlib import Path import itertools nbname = '' try: nbname = __vsc_ipynb_file__ except: if 'COCALC_JUPYTER_FILENAME' in os.environ: nbname = os.environ['COCALC_JUPYTER_FILENAME'] title_ = Path(nbname).stem.replace('-', '_').title() IFrame(f'https://discopal.ispras.ru/index.php?title=Hardprob/{title_}&useskin=cleanmonobook', width=1280, height=300)
# не знаю как правильн генерировать граф с соблюдением неравенства треугольника поэтому генерирую рандомные точки def dist(a, b): return int(np.ceil(np.sqrt((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2)))
def out_of_verts(v, m, edges): for i in range(0, m): if i == v: continue if edges[v][i] == 1000000000: return False return True
def generate_random_graph(m, n): if n < m - 1 or n > m*(m-1) / 2: return [[]] vertexes = [(random.uniform(-100, 100), random.uniform(-100, 100)) for _ in range(m)] edges = [[1000000000] * m for _ in range(m)] edges[0][1] = edges[1][0] = dist(vertexes[0], vertexes[1]) # обеспечение связности графа. поскольку вершины сами по себе рандомны, то можно идти вот так подряд - чтобы всегда был путь for i in range(2, m): edges[i][i - 1] = edges[i - 1][i] = dist(vertexes[i], vertexes[i - 1]) #это чтобы всегда был хоть один замкнутый путь edges[0][m - 1] = edges[m - 1][0] = dist(vertexes[0], vertexes[m - 1]) edges_left = n - m for _ in range(0, edges_left): v1 = randint(0, m - 1) while out_of_verts(v1, m, edges): v1 = randint(0, m - 1) v2 = randint(0, m - 1) while v2 == v1 or edges[v1][v2] < 1000000000: v2 = randint(0, m - 1) edges[v1][v2] = edges[v2][v1] = dist(vertexes[v2], vertexes[v1]) return edges
def get_model(edges): m = pyo.ConcreteModel() N = len(edges) m.J = pyo.RangeSet(0, N-1) # m.J = edge m.N = N m.IJ = m.J * m.J m.x = pyo.Var(m.IJ, domain=pyo.Binary) # m.x = pyo.Var(m.J, m.J, domain=pyo.Binary) m.edges = edges m.obj = pyo.Objective(expr = sum(m.edges[i][j] * m.x[i, j] for i, j in m.IJ), sense=pyo.minimize) @m.Constraint(m.J) def не_должно_быть_селф_циклов(m, i): return m.x[i, i] == 0 @m.Constraint(m.J) def только_один_вход(m, i): return sum(m.x[i, ...]) == 1 @m.Constraint(m.J) def только_один_выход(m, i): return sum(m.x[..., i]) == 1 m.I1 = pyo.RangeSet(1, N-1) m.IJ1 = pyo.Set(dimen=2, initialize=itertools.permutations(list(m.I1), 2)) m.u = pyo.Var(m.I1, bounds=(0, N - 2)) @m.Constraint(m.IJ1) def MillerTuckerZemlin(m, i: m.I1, j: m.I1): return m.u[i] - m.u[j] + m.N * m.x[i, j] <= m.N - 1 return m
edges = generate_random_graph(5, 10)
model = get_model(edges)
model.pprint()
2 Set Declarations IJ : Size=1, Index=None, Ordered=True Key : Dimen : Domain : Size : Members None : 2 : J*J : 25 : {(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (3, 0), (3, 1), (3, 2), (3, 3), (3, 4), (4, 0), (4, 1), (4, 2), (4, 3), (4, 4)} IJ1 : Size=1, Index=None, Ordered=Insertion Key : Dimen : Domain : Size : Members None : 2 : Any : 12 : {(1, 2), (1, 3), (1, 4), (2, 1), (2, 3), (2, 4), (3, 1), (3, 2), (3, 4), (4, 1), (4, 2), (4, 3)} 2 RangeSet Declarations I1 : Dimen=1, Size=4, Bounds=(1, 4) Key : Finite : Members None : True : [1:4] J : Dimen=1, Size=5, Bounds=(0, 4) Key : Finite : Members None : True : [0:4] 2 Var Declarations u : Size=4, Index=I1 Key : Lower : Value : Upper : Fixed : Stale : Domain 1 : 0 : None : 3 : False : True : Reals 2 : 0 : None : 3 : False : True : Reals 3 : 0 : None : 3 : False : True : Reals 4 : 0 : None : 3 : False : True : Reals x : Size=25, Index=IJ Key : Lower : Value : Upper : Fixed : Stale : Domain (0, 0) : 0 : None : 1 : False : True : Binary (0, 1) : 0 : None : 1 : False : True : Binary (0, 2) : 0 : None : 1 : False : True : Binary (0, 3) : 0 : None : 1 : False : True : Binary (0, 4) : 0 : None : 1 : False : True : Binary (1, 0) : 0 : None : 1 : False : True : Binary (1, 1) : 0 : None : 1 : False : True : Binary (1, 2) : 0 : None : 1 : False : True : Binary (1, 3) : 0 : None : 1 : False : True : Binary (1, 4) : 0 : None : 1 : False : True : Binary (2, 0) : 0 : None : 1 : False : True : Binary (2, 1) : 0 : None : 1 : False : True : Binary (2, 2) : 0 : None : 1 : False : True : Binary (2, 3) : 0 : None : 1 : False : True : Binary (2, 4) : 0 : None : 1 : False : True : Binary (3, 0) : 0 : None : 1 : False : True : Binary (3, 1) : 0 : None : 1 : False : True : Binary (3, 2) : 0 : None : 1 : False : True : Binary (3, 3) : 0 : None : 1 : False : True : Binary (3, 4) : 0 : None : 1 : False : True : Binary (4, 0) : 0 : None : 1 : False : True : Binary (4, 1) : 0 : None : 1 : False : True : Binary (4, 2) : 0 : None : 1 : False : True : Binary (4, 3) : 0 : None : 1 : False : True : Binary (4, 4) : 0 : None : 1 : False : True : Binary 1 Objective Declarations obj : Size=1, Index=None, Active=True Key : Active : Sense : Expression None : True : minimize : 1000000000*x[0,0] + 169*x[0,1] + 188*x[0,2] + 50*x[0,3] + 153*x[0,4] + 169*x[1,0] + 1000000000*x[1,1] + 117*x[1,2] + 125*x[1,3] + 105*x[1,4] + 188*x[2,0] + 117*x[2,1] + 1000000000*x[2,2] + 140*x[2,3] + 36*x[2,4] + 50*x[3,0] + 125*x[3,1] + 140*x[3,2] + 1000000000*x[3,3] + 105*x[3,4] + 153*x[4,0] + 105*x[4,1] + 36*x[4,2] + 105*x[4,3] + 1000000000*x[4,4] 4 Constraint Declarations MillerTuckerZemlin : Size=12, Index=IJ1, Active=True Key : Lower : Body : Upper : Active (1, 2) : -Inf : u[1] - u[2] + 5*x[1,2] : 4.0 : True (1, 3) : -Inf : u[1] - u[3] + 5*x[1,3] : 4.0 : True (1, 4) : -Inf : u[1] - u[4] + 5*x[1,4] : 4.0 : True (2, 1) : -Inf : u[2] - u[1] + 5*x[2,1] : 4.0 : True (2, 3) : -Inf : u[2] - u[3] + 5*x[2,3] : 4.0 : True (2, 4) : -Inf : u[2] - u[4] + 5*x[2,4] : 4.0 : True (3, 1) : -Inf : u[3] - u[1] + 5*x[3,1] : 4.0 : True (3, 2) : -Inf : u[3] - u[2] + 5*x[3,2] : 4.0 : True (3, 4) : -Inf : u[3] - u[4] + 5*x[3,4] : 4.0 : True (4, 1) : -Inf : u[4] - u[1] + 5*x[4,1] : 4.0 : True (4, 2) : -Inf : u[4] - u[2] + 5*x[4,2] : 4.0 : True (4, 3) : -Inf : u[4] - u[3] + 5*x[4,3] : 4.0 : True columns_check : Size=5, Index=J, Active=True Key : Lower : Body : Upper : Active 0 : 1.0 : x[0,0] + x[1,0] + x[2,0] + x[3,0] + x[4,0] : 1.0 : True 1 : 1.0 : x[0,1] + x[1,1] + x[2,1] + x[3,1] + x[4,1] : 1.0 : True 2 : 1.0 : x[0,2] + x[1,2] + x[2,2] + x[3,2] + x[4,2] : 1.0 : True 3 : 1.0 : x[0,3] + x[1,3] + x[2,3] + x[3,3] + x[4,3] : 1.0 : True 4 : 1.0 : x[0,4] + x[1,4] + x[2,4] + x[3,4] + x[4,4] : 1.0 : True lines_check : Size=5, Index=J, Active=True Key : Lower : Body : Upper : Active 0 : 1.0 : x[0,0] + x[0,1] + x[0,2] + x[0,3] + x[0,4] : 1.0 : True 1 : 1.0 : x[1,0] + x[1,1] + x[1,2] + x[1,3] + x[1,4] : 1.0 : True 2 : 1.0 : x[2,0] + x[2,1] + x[2,2] + x[2,3] + x[2,4] : 1.0 : True 3 : 1.0 : x[3,0] + x[3,1] + x[3,2] + x[3,3] + x[3,4] : 1.0 : True 4 : 1.0 : x[4,0] + x[4,1] + x[4,2] + x[4,3] + x[4,4] : 1.0 : True zero_at_diag : Size=5, Index=J, Active=True Key : Lower : Body : Upper : Active 0 : 0.0 : x[0,0] : 0.0 : True 1 : 0.0 : x[1,1] : 0.0 : True 2 : 0.0 : x[2,2] : 0.0 : True 3 : 0.0 : x[3,3] : 0.0 : True 4 : 0.0 : x[4,4] : 0.0 : True 11 Declarations: J IJ x obj zero_at_diag lines_check columns_check I1 IJ1 u MillerTuckerZemlin
solver = pyo.SolverFactory('cbc') solver.solve(model).write() res = model.obj() model.x.extract_values()
# ========================================================== # = Solver Results = # ========================================================== # ---------------------------------------------------------- # Problem Information # ---------------------------------------------------------- Problem: - Name: unknown Lower bound: 477.0 Upper bound: 477.0 Number of objectives: 1 Number of constraints: 22 Number of variables: 24 Number of binary variables: 25 Number of integer variables: 25 Number of nonzeros: 20 Sense: minimize # ---------------------------------------------------------- # Solver Information # ---------------------------------------------------------- Solver: - Status: ok User time: -1.0 System time: 0.28 Wallclock time: 0.06 Termination condition: optimal Termination message: Model was solved to optimality (subject to tolerances), and an optimal solution is available. Statistics: Branch and bound: Number of bounded subproblems: 0 Number of created subproblems: 0 Black box: Number of iterations: 9 Error rc: 0 Time: 0.11193680763244629 # ---------------------------------------------------------- # Solution Information # ---------------------------------------------------------- Solution: - number of solutions: 0 number of solutions displayed: 0
{(0, 0): 0.0, (0, 1): 1.0, (0, 2): 0.0, (0, 3): 0.0, (0, 4): 0.0, (1, 0): 0.0, (1, 1): 0.0, (1, 2): 1.0, (1, 3): 0.0, (1, 4): 0.0, (2, 0): 0.0, (2, 1): 0.0, (2, 2): 0.0, (2, 3): 0.0, (2, 4): 1.0, (3, 0): 1.0, (3, 1): 0.0, (3, 2): 0.0, (3, 3): 0.0, (3, 4): 0.0, (4, 0): 0.0, (4, 1): 0.0, (4, 2): 0.0, (4, 3): 1.0, (4, 4): 0.0}
def solveByPyomo(edges): model = get_model(edges) solver = pyo.SolverFactory('cbc') solver.solve(model) res = model.obj() return model.x.extract_values()
def modelResToPerm(dictres, m): res = [0]*m current = 0 for i in range(1, m): for item, v in dictres.items(): if item[0] == current and v == 1.0: res[i] = item[1] current = item[1] break return res
perm = modelResToPerm(model.x.extract_values(), 5)
def sumFromPermutation(edges, perm): res = edges[0][len(perm) - 1] for i in range(len(perm) - 1): res += edges[i][i+1] return res
sumFromPermutation(edges, perm)
684
def primitiveSolving(edges): m = len(edges) res = 1000000000 for perm in itertools.permutations(list(range(0, m)), m): res = min( sumFromPermutation(edges, perm) , res) return res
def primitiveTesting(n=100): for i in range(n): m = randint(5, 10) ed = randint(m, (m*(m-1)) // 2) edges = generate_random_graph(m, ed) if sumFromPermutation(edges, modelResToPerm(solveByPyomo(edges), m)) == primitiveSolving(edges): print(f'Test{i}: OK') else: print(f'Test{i}: Error | m = {m} | edg = {ed}') print(edges) print(solveByPyomo(edges)) print(primitiveSolving(edges))